#include <rapidxml_print.hpp>
#include "Processor.h"

#ifdef _WIN32
#ifdef _DEBUG
   #include <crtdbg.h>
   #undef THIS_FILE
   static char THIS_FILE[] = __FILE__;
   #define new       new( _NORMAL_BLOCK, __FILE__, __LINE__)
   #define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#endif

#include <iostream>
using namespace std;
using namespace rapidxml;

// Constructor
Processor::Processor(NetworkManager &nm) {
	// Remember a link to the NetworkManager
	this->nm = &nm;
}

// Main action
void Processor::start(Connection *conn) {
	ParamManager pm;
	char data[DEFAULT_BUFLEN];
	

	// Parse out the passed in parameters
	pm.init(conn->bm->head->data, "|");

	// Take action accordingly
	if(!strcmp(pm.get("cmd"), "task_add")) {
		Task *task = this->tm.add();
		if(task == NULL) {
			// send response
			sprintf(data, "cmd=task_add_reply|status=1\r\n");
			conn->write(data);
		}
		else {
			// send response
			sprintf(data, "cmd=task_add_reply|id=%s|status=0\r\n", task->id);
			conn->write(data);
		}
	}
	else if(!strcmp(pm.get("cmd"), "task_del")) {
		Task *task = this->tm.get(pm.get("id"));
		if(task == NULL) {
			// send response
			sprintf(data, "cmd=task_del_reply|id=%s|status=1\r\n", pm.get("id"));
			conn->write(data);
		}
		else {
			// provide a notice if the task had yet to finish
			if(task->state == ACTIVE) {
				// send notice to all clients the task is complete
				sprintf(data, "cmd=notice|id=%s|info=2\r\n", pm.get("id"));
				nm->write("port", conn->settings.get("port"), data);
			}

			// delete the task
			this->tm.del(task->id);

			// send response
			sprintf(data, "cmd=task_del_reply|id=%s|status=0\r\n", pm.get("id"));
			conn->write(data);
		}
	}
	else if(!strcmp(pm.get("cmd"), "task_set")) {
		Task *task = this->tm.get(pm.get("id"));
		if(task == NULL) {
			// send response
			sprintf(data, "cmd=task_set_reply|id=%s|status=1|data=%s\r\n", pm.get("id"), pm.get("data"));
			conn->write(data);
		}
		else {
			task->sm.set(pm.get("data"), pm.get("value"));

			// send response
			sprintf(data, "cmd=task_set_reply|id=%s|status=0|data=%s\r\n", task->id, pm.get("data"));
			conn->write(data);

			// if the task is currently active then broadcast this change
			if(task->state == ACTIVE) {
				sprintf(data, "cmd=task_detail_item|id=%s|data=%s|value=%s\r\n", task->id, pm.get("data"), pm.get("value"));
				nm->write("port", conn->settings.get("port"), data);
			}
		}
	}
	else if(!strcmp(pm.get("cmd"), "task_start")) {
		Task *task = this->tm.get(pm.get("id"));
		if(task == NULL) {
			// send response
			sprintf(data, "cmd=task_start_reply|id=%s|status=1\r\n", pm.get("id"));
			conn->write(data);
		}
		else {
			// Initialize first block: build one initial large block
			task->bm.init(	task->sm.get("todo_count"),
							task->sm.get("block_size"),
							atoi(task->sm.get("timeout"))
							);

			// record a time stamp of this event
			sprintf(data, "%d", time(NULL));
			task->sm.set("timestamp", data);
//					task->sw.export("main");
			sprintf(data, "utc_%d", time(NULL));
			task->sm.set("timestamp_utc", data);

			// change status to active
			task->state = ACTIVE;
			task->sw.start("main");

			// default priority if one has not already been set
			if(!task->sm.isSet("priority")) {
				task->sm.set("priority", "3");
			}

			// send response
			sprintf(data, "cmd=task_start_reply|id=%s|status=0\r\n", task->id);
			conn->write(data);

			// send notice to all clients of the newly available task
			sprintf(data, "cmd=notice|id=%s|info=1|todo=%s\r\n", task->id, task->sm.get("todo"));
			nm->write("port", conn->settings.get("port"), data);
		}
	}
	else if(!strcmp(pm.get("cmd"), "task_list")) {
		// send response
		this->tm.list(conn, pm.get("status"));
	}
	else if(!strcmp(pm.get("cmd"), "task_detail")) {
		Task *task = this->tm.get(pm.get("id"));
		if(task == NULL) {
			// send response
			sprintf(data, "cmd=task_detail_reply|id=%s|status=1\r\n", pm.get("id"));
			conn->write(data);
		}
		else {
			int count = 0;

			// loop and count up number of items
			Param *slider = task->sm.head;
			while(slider != NULL) {
				count ++;

				slider = slider->next;
			}

			// send response
			sprintf(data, "cmd=task_detail_reply|id=%s|status=0|count=%d\r\n", task->id, count);
			conn->write(data);

			// loop again, this time sending each item
			slider = task->sm.head;
			while(slider != NULL) {
				// send response
				sprintf(data, "cmd=task_detail_item|id=%s|data=%s|value=%s\r\n", task->id, slider->data, slider->value);
				conn->write(data);

				slider = slider->next;
			}
		}
	}
	else if(!strcmp(pm.get("cmd"), "task_get")) {
		Task *task = this->tm.get(pm.get("id"));
		if(task == NULL) {
			sprintf(data, "cmd=task_get_reply|id=%s|status=1\r\n", pm.get("id"));
			conn->write(data);
		}
		else {
			// obtain number of completed todos
			char *complete = task->bm.getStatus();

			// send response
			switch(task->state) {
			case PENDING:
				sprintf(data, "cmd=task_get_reply|id=%s|status=0|timestamp=%d|priority=%s|state=%d|todo=%s|complete=%s|total=%s|seconds=%.2f\r\n",
							task->id, 0, task->sm.get("priority"), task->state, task->sm.get("todo"),
							complete, task->sm.get("todo_count"), 0);
				break;
			case ACTIVE:
				sprintf(data, "cmd=task_get_reply|id=%s|status=0|timestamp=%s|priority=%s|state=%d|todo=%s|complete=%s|total=%s|seconds=%.2f\r\n",
							task->id, task->sm.get("timestamp"), task->sm.get("priority"), task->state, task->sm.get("todo"),
							complete, task->sm.get("todo_count"), task->sw.get_time_so_far("main"));
				break;
			case COMPLETE:
				sprintf(data, "cmd=task_get_reply|id=%s|status=0|timestamp=%s|priority=%s|state=%d|todo=%s|complete=%s|total=%s|seconds=%.2f\r\n",
							task->id, task->sm.get("timestamp"), task->sm.get("priority"), task->state, task->sm.get("todo"),
							complete, task->sm.get("todo_count"), task->sw.get_total_time("main"));
				break;
			}
			conn->write(data);

			// free up the resources used by the bignum complete count
			OPENSSL_free(complete);
		}
	}
	else if(!strcmp(pm.get("cmd"), "client_list")) {
		Connection *c;
		int count = 0;

		// loop counting up the number of clients
		this->nm->cm->reset();
		while(c = this->nm->cm->getNext()) {
			if(!c->settings.isSet("isListen")) {
				count ++;
			}
		}

		// send response
		sprintf(data, "cmd=client_list_reply|count=%d\r\n", count);
		conn->write(data);

		// loop clients sending back list
		this->nm->cm->reset();
		while(c = this->nm->cm->getNext()) {
			if(!c->settings.isSet("isListen")) {
				// send response
				sprintf(data, "cmd=client_list_item|host=%s\r\n", c->settings.get("host"));
				conn->write(data);
			}
		}				
	}
	else if(!strcmp(pm.get("cmd"), "client_set")) {
		conn->settings.set(pm.get("data"), pm.get("value"));

		// send response
		sprintf(data, "cmd=client_set_reply|status=0|data=%s\r\n", pm.get("data"));
		conn->write(data);			
	}
	else if(!strcmp(pm.get("cmd"), "block_checkout")) {
		Task *task = this->tm.get(pm.get("id"));
		if(task == NULL) {
			sprintf(data, "cmd=block_checkout_reply|id=%s|status=2|detail=unknown_id\r\n", pm.get("id"));
			conn->write(data);
		}
		else {
			Block *block = task->bm.get();
			if(block == NULL) {
				// block not available, client should retry later & watch for a notice that the task is finished
				sprintf(data, "cmd=block_checkout_reply|id=%s|status=1|detail=busy\r\n", task->id);
				conn->write(data);		
			}
			else {
				// send response
				char *index = BN_bn2dec(block->index);
				char *count = BN_bn2dec(block->count);
				sprintf(data, "cmd=block_checkout_reply|id=%s|status=0|index=%s|size=%s\r\n", task->id, index, count);
				conn->write(data);		
				OPENSSL_free(index);
				OPENSSL_free(count);
			}
		}
	}
	else if(!strcmp(pm.get("cmd"), "block_checkin")) {
		Task *task = this->tm.get(pm.get("id"));
		if(task == NULL) {
			sprintf(data, "cmd=block_checkin_reply|id=%s|status=1\r\n", pm.get("id"));
			conn->write(data);
		}
		else {
			if(task->bm.set( pm.get("index")) ) {
				// send response
				sprintf(data, "cmd=block_checkin_reply|id=%s|status=0\r\n", task->id);
				conn->write(data);
			}
			// this else doesn't actually do anything bm.set never fails
			else {
				// send response
				sprintf(data, "cmd=block_checkin_reply|id=%s|status=1\r\n", task->id);
				conn->write(data);
			}
					
			// check if the task is complete
			if(task->bm.isComplete()) {
				// mark the task as complete
				task->state = COMPLETE;
				task->sw.stop("main");

				// send notice to all clients if the task is complete
				sprintf(data, "cmd=notice|id=%s|info=2\r\n", task->id);
				nm->write("port", conn->settings.get("port"), data);


				// also if our completed count is > max, delete the oldest task
				int count = 0;
				task = this->tm.head;
				while(task) {
					if(task->state == COMPLETE) {
						count ++;
					}
					task = task->next;
				}
				// keeps a history of 50
				if(count > 10) {
					// find the oldest task and delete it
					task = this->tm.head;
					while(task) {
						if(task->state == COMPLETE) {
							this->tm.del(task->id);
							break;
						}
						task = task->next;
					}
				}
			}
		}
	}
	else {
		// see if the data parses successfully as xml
		char *text = new char[strlen(conn->bm->head->data) + 1];
		strcpy(text, conn->bm->head->data);

		xml_document<> doc;
		try {
			doc.parse<0>(text);
					
			// standard way to use rapidxml, though there will only ever be one packet
			if(doc.first_node("packet") && doc.first_node("packet")->first_node("cmd")) {	// validate
				xml_node<> *packet = doc.first_node("packet");
				xml_node<> *cmd = packet->first_node("cmd");

				cout << "(XML) cmd = '" << cmd->value() << "'" << endl;
				if(!strcmp(cmd->value(), "task_add"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);

					Task *task = this->tm.add();
					if(task) {
						// send response
						sprintf(data, "%s<cmd type='response' status='0'>task_add</cmd>", data);
						sprintf(data, "%s<id>%s</id>", data, task->id);
					}
					else {
						// failure response
						sprintf(data, "%s<cmd type='response' status='1'>task_add</cmd>", data);
					}

					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "task_del"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);

					if(packet->first_node("id")) {	// validate
						xml_node<> *task_id = packet->first_node("id");

						Task *task = this->tm.get(task_id->value());
						if(task) {							
							// delete the task
							this->tm.del(task->id);

							// send response
							sprintf(data, "%s<cmd type='response' status='0'>task_del</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());

							// provide a notice if the task had yet to finish
							if(task->state == ACTIVE) {
								// send notice to all clients the task is complete
								char data2[DEFAULT_BUFLEN];
								sprintf(data2, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
								sprintf(data2, "%s<packet>", data2);
								sprintf(data2, "%s<cmd type='notice'>task_del</cmd>", data2);
								sprintf(data2, "%s<id>%s</id>", data2, task_id->value());
								sprintf(data2, "%s</packet>", data2);
								nm->write("port", conn->settings.get("port"), data2);
							}
						}
						else {
							// failure response, id not found
							sprintf(data, "%s<cmd type='response' status='2'>task_del</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());
						}
					}
					else {
						// failure response, missing parameters
						sprintf(data, "%s<cmd type='response' status='1'>task_del</cmd>", data);
					}

					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "task_detail_set"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);

					if(packet->first_node("id")
						&& packet->first_node("data")
						&& packet->first_node("value")) {	// validate

						xml_node<> *task_id = packet->first_node("id");
						xml_node<> *_data = packet->first_node("data");
						xml_node<> *_value = packet->first_node("value");

						Task *task = this->tm.get(task_id->value());
						if(task) {
							// set new data/value pair
							task->sm.set(_data->value(), _value->value());

							sprintf(data, "%s<cmd type='response' status='0'>task_detail_set</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());
							sprintf(data, "%s<data>%s</data>", data, _data->value());

							// if the task is currently active then broadcast this change
							if(task->state == ACTIVE) {
								char data2[DEFAULT_BUFLEN];
								sprintf(data2, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
								sprintf(data2, "%s<packet>", data2);
								sprintf(data2, "%s<cmd type='notice'>task_detail_set</cmd>", data2);
								sprintf(data2, "%s<id>%s</id>", data2, task_id->value());
								sprintf(data2, "%s<data>%s</data>", data2, _data->value());
								sprintf(data2, "%s<value>%s</value>", data2, _value->value());
								sprintf(data2, "%s</packet>\r\n", data2);
								nm->write("port", conn->settings.get("port"), data2);
							}
						}
						else {
							// failure response, id not found
							sprintf(data, "%s<cmd type='response' status='2'>task_detail_set</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());
						}
					}
					else {
						// failure response, missing parameters
						sprintf(data, "%s<cmd type='response' status='1'>task_detail_set</cmd>", data);
					}

					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "task_start"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);

					if(packet->first_node("id")) {	// validate
						xml_node<> *task_id = packet->first_node("id");

						Task *task = tm.get(task_id->value());
						if(task) {
							// Initialize first block: build one initial large block
							task->bm.init(	task->sm.get("todo_count"),
											task->sm.get("block_size"),
											atoi(task->sm.get("timeout"))
											);

							// record a time stamp of this event
							char data2[DEFAULT_BUFLEN];
							sprintf(data2, "%d", time(NULL));
							task->sm.set("timestamp", data2);
							sprintf(data2, "utc_%d", time(NULL));
							task->sm.set("timestamp_utc", data2);

							// change status to active
							task->state = ACTIVE;
							task->sw.start("main");

							// default priority if one has not already been set
							if(!task->sm.isSet("priority")) {
								task->sm.set("priority", "3");
							}

							// send response
							sprintf(data, "%s<cmd type='response' status='0'>task_start</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());

							// send notice to all clients of the newly available task
							sprintf(data2, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
							sprintf(data2, "%s<packet>", data2);
							sprintf(data2, "%s<cmd type=\"notice\">task_start</cmd>", data2);
							sprintf(data2, "%s<id>%s</id>", data2, task_id->value());
							sprintf(data2, "%s<todo>%s</todo>", data2, task->sm.get("todo"));
							sprintf(data2, "%s</packet>\r\n", data2);
							nm->write("port", conn->settings.get("port"), data2);
						}
						else {
							// failure response, id not found
							sprintf(data, "%s<cmd type='response' status='2'>task_start</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());
						}
					}
					else {
						// failure response, missing parameters
						sprintf(data, "%s<cmd type='response' status='1'>task_start</cmd>", data);
					}

					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "task_list"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);

					xml_node<> *state = packet->first_node("state");	// not required
					
					sprintf(data, "%s<cmd type='response' status='0'>task_list</cmd>", data);
					if(state) {
						sprintf(data, "%s<state>%s</state>", data, state->value());
					}
					sprintf(data, "%s<task_list>", data);
					conn->write(data);

					Task *task = tm.head;
					while(task) {
						bool match = false;

						if(state) {
							// only include tasks with the requested state
							switch(task->state) {
								case PENDING:
									if(!stricmp(state->value(), "PENDING")) {
										match = true;
									}
									break;
								case ACTIVE:
									if(!stricmp(state->value(), "ACTIVE")) {
										match = true;
									}
									break;
								case COMPLETE:
									if(!stricmp(state->value(), "COMPLETE")) {
										match = true;
									}
									break;
							}
						}
						else {
							match = true;
						}

						if(match) {
							switch(task->state) {
							case PENDING:
								sprintf(data, "<task>");
									sprintf(data, "%s<id>%s</id>", data, task->id);
									sprintf(data, "%s<timestamp>%s</timestamp>", data, "0");
									sprintf(data, "%s<priority>%s</priority>", data, task->sm.get("priority"));
									sprintf(data, "%s<state>%d</state>", data, task->state);
									sprintf(data, "%s<todo>%s</todo>", data, task->sm.get("todo"));
									sprintf(data, "%s<complete>%s</complete>", data, task->bm.getStatus());
									sprintf(data, "%s<total>%s</total>", data, task->sm.get("todo_count"));
									sprintf(data, "%s<seconds>%.2f</seconds>", data, 0);
								sprintf(data, "%s</task>", data);
								break;
							case ACTIVE:
							case COMPLETE:
								sprintf(data, "<task>");
									sprintf(data, "%s<id>%s</id>", data, task->id);
									sprintf(data, "%s<timestamp>%s</timestamp>", data, task->sm.get("timestamp"));
									sprintf(data, "%s<priority>%s</priority>", data, task->sm.get("priority"));
									sprintf(data, "%s<state>%d</state>", data, task->state);
									sprintf(data, "%s<todo>%s</todo>", data, task->sm.get("todo"));
									sprintf(data, "%s<complete>%s</complete>", data, task->bm.getStatus());
									sprintf(data, "%s<total>%s</total>", data, task->sm.get("todo_count"));
									sprintf(data, "%s<seconds>%.2f</seconds>", data, task->sw.get_time_so_far("main"));
								sprintf(data, "%s</task>", data);
								break;
							}
						
							conn->write(data);
						}

						task = task->next;
					}

					sprintf(data, "</task_list>");
					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "task_detail"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);
					conn->write(data);

					if(packet->first_node("id")) {	// validate
						Task *task = tm.get( packet->first_node("id")->value() );
						if(task) {
							sprintf(data, "<cmd type='response' status='0'>task_detail</cmd>");
							sprintf(data, "%s<id>%s</id>", data, task->id);
							sprintf(data, "%s<task_detail>", data);
							conn->write(data);

							Param *slider = task->sm.head;
							while(slider != NULL) {
								// send response
								sprintf(data, "<%s>%s</%s>", slider->data, slider->value, slider->data);
								conn->write(data);

								slider = slider->next;
							}

							sprintf(data, "</task_detail>");
						}
						else {
							// failure response, id not found
							sprintf(data, "%s<cmd type='response' status='2'>task_detail</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, packet->first_node("id")->value());
						}
					}
					else {
						// failure response, missing parameters
						sprintf(data, "%s<cmd type='response' status='1'>task_detail</cmd>", data);
					}
					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "task_get"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);

					if(packet->first_node("id")) {	// validate
						xml_node<> *task_id = packet->first_node("id");

						Task *task = tm.get(task_id->value());
						if(task) {

							sprintf(data, "%s<cmd type='response' status='0'>task_get</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());
							conn->write(data);

							switch(task->state) {
							case PENDING:
								sprintf(data, "<task>");
									sprintf(data, "%s<id>%s</id>", data, task->id);
									sprintf(data, "%s<timestamp>%s</timestamp>", data, "0");
									sprintf(data, "%s<priority>%s</priority>", data, task->sm.get("priority"));
									sprintf(data, "%s<state>%d</state>", data, task->state);
									sprintf(data, "%s<todo>%s</todo>", data, task->sm.get("todo"));
									sprintf(data, "%s<complete>%s</complete>", data, task->bm.getStatus());
									sprintf(data, "%s<total>%s</total>", data, task->sm.get("todo_count"));
									sprintf(data, "%s<seconds>%.2f</seconds>", data, 0);
								sprintf(data, "%s</task>", data);
								break;
							case ACTIVE:
							case COMPLETE:
								sprintf(data, "<task>");
									sprintf(data, "%s<id>%s</id>", data, task->id);
									sprintf(data, "%s<timestamp>%s</timestamp>", data, task->sm.get("timestamp"));
									sprintf(data, "%s<priority>%s</priority>", data, task->sm.get("priority"));
									sprintf(data, "%s<state>%d</state>", data, task->state);
									sprintf(data, "%s<todo>%s</todo>", data, task->sm.get("todo"));
									sprintf(data, "%s<complete>%s</complete>", data, task->bm.getStatus());
									sprintf(data, "%s<total>%s</total>", data, task->sm.get("todo_count"));
									sprintf(data, "%s<seconds>%.2f</seconds>", data, task->sw.get_time_so_far("main"));
								sprintf(data, "%s</task>", data);
								break;
							}
						}
						else {
							// failure response, id not found
							sprintf(data, "%s<cmd type='response' status='2'>task_get</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());
						}
					}
					else {
						// failure response, missing parameters
						sprintf(data, "%s<cmd type='response' status='1'>task_get</cmd>", data);
					}
										
					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "client_list"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);
					sprintf(data, "%s<cmd type='response' status='0'>client_list</cmd>", data);
					sprintf(data, "%s<client_list>", data);
					conn->write(data);

					Connection *c;

					// loop clients sending back list
					this->nm->cm->reset();
					while(c = this->nm->cm->getNext()) {
						if(!c->settings.isSet("isListen")) {
							// send response
							sprintf(data, "<client>%s</client>\r\n", c->settings.get("host"));
							conn->write(data);
						}
					}				
					
					sprintf(data, "</client_list>", data);
					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "client_set"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);

					if(packet->first_node("data")
						&& packet->first_node("value")) {	// validate

						xml_node<> *_data = packet->first_node("data");
						xml_node<> *_value = packet->first_node("value");


						conn->settings.set(_data->value(), _value->value());

						sprintf(data, "%s<cmd type='response' status='0'>client_set</cmd>", data);
						sprintf(data, "%s<data>%s</data>", data, _data->value());
					}
					else {
						// failure reply, missing parameters
						sprintf(data, "%s<cmd type='response' status='1'>client_set</cmd>", data);
					}

					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "block_checkout"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);

					if(packet->first_node("id")) {
						xml_node<> *task_id = packet->first_node("id");

						Task *task = tm.get(task_id->value());
						if(task) {
							Block *block = task->bm.get();
							if(block) {
								// send response
								char *index = BN_bn2dec(block->index);
								char *count = BN_bn2dec(block->count);
								sprintf(data, "%s<cmd type='response' status='0'>block_checkout</cmd>", data);
								sprintf(data, "%s<id>%s</id>", data, task_id->value());
								sprintf(data, "%s<block>", data);
								sprintf(data, "%s<index>%s</index>", data, index);
								sprintf(data, "%s<size>%s</size>", data, count);
								sprintf(data, "%s</block>", data);
								OPENSSL_free(index);
								OPENSSL_free(count);
							}
							else {
								// failure response, busy, no blocks available to hand out
								// client should retry after a delay & in the meantime, watch for a notice that the task is finished
								sprintf(data, "%s<cmd type='response' status='3'>block_checkout</cmd>", data);
								sprintf(data, "%s<id>%s</id>", data, task_id->value());
							}
						}
						else {
							// failure response, id not found
							sprintf(data, "%s<cmd type='response' status='2'>block_checkout</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());
						}
					}
					else {
						// failure response, missing parameters
						sprintf(data, "%s<cmd type='response' status='1'>block_checkout</cmd>", data);
					}

					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);
				}
				else if(!strcmp(cmd->value(), "block_checkin"))  {
					sprintf(data, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
					sprintf(data, "%s<packet>", data);

					if(packet->first_node("id")
						&& packet->first_node("index")) {	// validate

						xml_node<> *task_id = packet->first_node("id");
						xml_node<> *_index = packet->first_node("index");

						Task *task = tm.get(task_id->value());
						if(task) {
							if(task->bm.set( _index->value() )) {
								sprintf(data, "%s<cmd type='response' status='0'>block_checkin</cmd>", data);
								sprintf(data, "%s<id>%s</id>", data, task_id->value());
								sprintf(data, "%s<index>%s</index>", data, _index->value());							

								// check if the task is complete
								if(task->bm.isComplete()) {
									// mark the task as complete
									task->state = COMPLETE;
									task->sw.stop("main");

									// send notice to all clients if the task is complete
									char data2[DEFAULT_BUFLEN];
									sprintf(data2, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
									sprintf(data2, "%s<packet>", data2);
									sprintf(data2, "%s<cmd type='notice'>task_complete</cmd>", data2);
									sprintf(data2, "%s<id>%s</id>", data2, task_id->value());
									sprintf(data2, "%s</packet>", data2);
									nm->write("port", conn->settings.get("port"), data2);

									// also if our completed count is > max, delete the oldest task
									int count = 0;
									task = this->tm.head;
									while(task) {
										if(task->state == COMPLETE) {
											count ++;
										}
										task = task->next;
									}
									// keeps a history of 50
									if(count > 10) {
										// find the oldest task and delete it
										task = this->tm.head;
										while(task) {
											if(task->state == COMPLETE) {
												this->tm.del(task->id);
												break;
											}
											task = task->next;
										}
									}
								}
							}
							else {
								// failure response, index not found
								sprintf(data, "%s<cmd type='response' status='3'>block_checkin</cmd>", data);
								sprintf(data, "%s<id>%s</id>", data, task_id->value());
								sprintf(data, "%s<index>%s</index>", data, _index->value());
							}
						}
						else {
							// failure response, id not found
							sprintf(data, "%s<cmd type='response' status='2'>block_checkin</cmd>", data);
							sprintf(data, "%s<id>%s</id>", data, task_id->value());
							sprintf(data, "%s<index>%s</index>", data, _index->value());
						}
					}
					else {
						// failure response, missing parameters
						sprintf(data, "%s<cmd type='response' status='1'>block_checkin</cmd>", data);
					}

					sprintf(data, "%s</packet>\r\n", data);
					conn->write(data);					
				}
				// [message] Possibilities:
				// 1. save message, launch drone, when the drone connects pass along the message
				//(2) client could say they are interested in messages related to task, only tell those registered
				// ... or, nothing, this could be a bad idea; but some sort of way to be instant seems needed to avoid probing
				else if(!strcmp(cmd->value(), "message_register"))  {
					if(packet->first_node("id")) {	// validate
						// for now, ignore
					}
				}
				else if(!strcmp(cmd->value(), "message"))  {
					if(packet->first_node("id")) {	// validate
						// for now, just echo back to all clients, not just registered, this is obviously something to fix
						nm->write("port", "2244", conn->bm->head->data);
						// todo: fix hard coded
					}
				}
			}
		} catch(rapidxml::parse_error code) {
#ifdef _DEBUG
//			cout << "(XML) Parse error failure" << endl;
			
			sprintf(data, "[Invalid Protocol] data ignored: '%s'\r\n", conn->bm->head->data);
			printf("%s", data);
#endif // _DEBUG
		}

		// cleanup allocated resources
		delete text;
	}
}